package com.emex.app.stuff.security;

import com.emex.app.shared.data.User;
import com.emex.app.stuff.provider.AuthenticationDataProvider;
import com.emex.app.stuff.security.ui.UserInfoStatusLine;
import java.net.Authenticator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.xml.ws.BindingProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 *
 * @author Sergey Karavaev
 */
public class SecurityManager {

    private static final Logger logger
            = LoggerFactory.getLogger(SecurityManager.class);
    private static SecurityManager instance = new SecurityManager();
    private String user;
    private String password;
    private User current;
    private String endpointAddress;
    private String protocol = "http://";

    /**
     * Private constructor. Do not create objects of this type directly.
     */
    private SecurityManager() {
    }

    /**
     * Get default SecurityManager.
     *
     * @return the single SecurityManager object
     */
    public static SecurityManager getDefault() {
        return instance;
    }

    /**
     * Authenticate user with username and password using WebService.
     *
     * @param username user name
     * @param password password (plain text)
     * @return true if authentication successful
     */
    public boolean wsLogin(String username, String password) {
        logger.debug("User " + username + " trying to login.");
        boolean authenticationSucceed = false;
        username = username.toLowerCase();
        try {
            // Set default authenticator to null to avoid
            // pop up of duplicate login dialog
            current = AuthenticationDataProvider.getInstance().authenticate(username, password);
            if (null != current) {
                authenticationSucceed = true;
            }
            //BindingProvider bp = AuthenticationDataProvider.getInstance().getBindingProvider();
            //bp.getRequestContext().put(BindingProvider.USERNAME_PROPERTY, username);
            //bp.getRequestContext().put(BindingProvider.PASSWORD_PROPERTY, password);
            Authenticator.setDefault(null);

            this.user = username;
            this.password = password;
        } catch (Exception ex) {
            logger.error("Unable to authenticate user: " + username, ex);
        }

        if (authenticationSucceed) {
            setUserInfoToStatusBar();
        }

        return authenticationSucceed;
    }

    /**
     * Set endpoint IP address and port
     *
     * @param bp Port binding provider
     */
    public void setEndpointAddress(BindingProvider bp) {
        logger.debug("setEndpointAddress(BindingProvider) called");
        String oldEndpAddr = bp.getRequestContext().get(BindingProvider.ENDPOINT_ADDRESS_PROPERTY).toString();
        final String expression = "^(([^:/\\\\?#]+):)?(//(([^:/\\\\?#]*)(?::([^/\\\\?#]*))?))?([^\\\\?#]*)(\\\\?([^#]*))?(#(.*))?$";
        Pattern pattern = Pattern.compile(expression);
        Matcher matcher = pattern.matcher(oldEndpAddr);
        if (matcher.find()) {
            String oldIpPort = matcher.group(4);
            final String newUrl = oldEndpAddr.replace(oldIpPort, endpointAddress);
            logger.debug("Endpoint URL after replacement: "
                    + newUrl);
            bp.getRequestContext().put(
                    BindingProvider.ENDPOINT_ADDRESS_PROPERTY, newUrl);
        }
    }

    /**
     * Secure web service request by adding login and password
     *
     * @param bp Port binding provider
     */
    public void secureRequest(BindingProvider bp) {
        logger.debug("secureRequest(BindingProvider) called");
        bp.getRequestContext().put(BindingProvider.USERNAME_PROPERTY, user);
        bp.getRequestContext().put(BindingProvider.PASSWORD_PROPERTY, password);
    }

    /**
     * Set currently logged in user information to the status bar of
     * application.
     */
    private void setUserInfoToStatusBar() {
        if (null != current) {
            UserInfoStatusLine.setUsername("Пользователь: "
                    + current.getSurname() + " "
                    + current.getName().charAt(0) + "."
                    + current.getPatronymic().charAt(0) + "."
                    + " (" + user + ")");
        } else {
            UserInfoStatusLine.setUsername("Пользователь: " + user);
        }
    }

    /**
     * Get server address.
     *
     * @return server address
     */
    public String getServerAddress() {
        return endpointAddress;
    }

    /**
     * Get current user's username.
     *
     * @return current user's username
     */
    public String getUsername() {
        return user;
    }

    /**
     * Get current user's password.
     *
     * @return current user's password
     */
    public String getPassword() {
        return password;
    }

    /**
     * Get current person object. Can return null in case there are no Person
     * object for current user.
     *
     * @return current user
     */
    public User getCurrentPerson() {
        return current;
    }

    /**
     * @param endpointAddress the endpointAddress to set
     */
    public void setEndpointAddress(String endpointAddress) {
        this.endpointAddress = endpointAddress;
    }

    /**
     * Get connection URL: protocol + IP + port Example: "http://1.1.1.1:8080"
     *
     * @return
     */
    public String getConnectionURL() {
        return protocol + endpointAddress;
    }
}
